<!DOCTYPE html>
<html lang="en">
<head>
	<title>three.js webgl - mirror</title>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
	<link type="text/css" rel="stylesheet" href="main.css">
	<style>
	</style>
</head>
<body>
<div id="container" style="width:100%;height:100vh;position:relative; overflow: hidden;"></div>
</div>
<!-- vertext shader a.k.a. pixel shader -->
<script id="vertexShader" type="x-shader/x-vertex">
    uniform vec3 viewVector;
	uniform float c;
	uniform float p;
	varying vec2 vUv;
	varying float intensity;

	void main() {
	 	vec3 v_normal = normalize(normalMatrix * normal);
		vec3 v_view = normalize(normalMatrix * viewVector);
		intensity = pow(c - dot(v_normal, v_view), p);
	 	vUv = uv;
	  	gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
	}
</script>
<script id="fragmentShader" type="x-shader/x-vertex">
	uniform sampler2D diffuse;
	uniform vec3 glowColor;
	varying float intensity;
	varying vec2 vUv;

	void main() {
		vec3 glow = glowColor * intensity;
	  	gl_FragColor = vec4(glow, 1.0)+ texture2D(diffuse, vUv);
	}
</script>
<!-- vertext shader a.k.a. pixel shader -->
<script id="vertexShader2" type="x-shader/x-vertex">
	varying vec2 vUv;
    attribute float percent;
    uniform float u_time;
    uniform float number;
    uniform float speed;
    uniform float length;
    varying float opacity;
    uniform float size;
    void main()
    {
        vUv = uv;
        vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
        float l = clamp(1.0-length,0.0,1.0);
        gl_PointSize = clamp(fract(percent*number + l - u_time*number*speed)-l ,0.0,1.) * size * (1./length);
        opacity = gl_PointSize/size;
        gl_Position = projectionMatrix * mvPosition;
    }
</script>
<!-- fragment shader a.k.a. pixel shader -->
<script id="fragmentShader2" type="x-shader/x-vertex">
	#ifdef GL_ES
    precision mediump float;
    #endif
    varying float opacity;
    uniform vec3 color;
    void main(){
        if(opacity <=0.2){
            discard;
        }
        gl_FragColor = vec4(color,1.0);
    }
</script>
<script type="module">
	import * as THREE from '../build/three.module.js';
	import { OrbitControls } from './jsm/controls/OrbitControls.js';
	import Stats from './jsm/libs/stats.module.js';
	import { Line2 } from './jsm/lines/Line2.js';
	import { LineMaterial } from './jsm/lines/LineMaterial.js';
	import { LineGeometry } from './jsm/lines/LineGeometry.js';
	import { TWEEN } from './jsm/libs/tween.module.min.js';
	import { GUI } from './jsm/libs/dat.gui.module.js';
	let renderer, camera, scene, gui, light, stats, controls, geometry, material, line, matLine, mesh, stars, uniforms;
	const Dom = document.querySelector( '#container' );
	const radius = 5;
	const width = Dom.clientWidth, height = Dom.clientHeight;
	const group = new THREE.Group();
	const groupDots = new THREE.Group();
	const groupLines = new THREE.Group();
	const groupHalo = new THREE.Group(); //卫星环+小卫星
	const aGroup = new THREE.Group();
	var initFlag = false;
	var WaveMeshArr = [];//所有波动光圈集合
	var planGeometry = new THREE.PlaneBufferGeometry( 1, 1 ); //默认在XOY平面上
	var globeTextureLoader = new THREE.TextureLoader();
	var map = new THREE.Object3D();
	var camaPositions = [
		{ x: 5, y: - 20, z: 200 }, //远处
		{ x: 0.5, y: - 2, z: 20 }//近处
	];
	var API = {
		c: 1.7,
		p: 2.3,
		color: 0x10105
	};
	var uniforms2 = {
		u_time: { value: 0.0 }
	};
	//模拟的空间坐标 已经通过经纬度转换了
	const posArr = [
		{ 'x': - 1.7049594735603837, 'y': 3.208354470512221, 'z': - 3.4350509144786985 },
		{ 'x': - 2.1965610576118175, 'y': 2.1955955192304506, 'z': - 3.9184792759587768 },
		{ 'x': - 2.2290975556080355, 'y': 2.6054406912933263, 'z': - 3.639066211507457 },
		{ 'x': 0.5738958419746141, 'y': - 0.44114968930852216, 'z': 4.9473255920938985 },
		{ 'x': - 0.9326350073394328, 'y': 2.8399222968004114, 'z': - 4.00812091773949 },
		{ 'x': 3.469198597393574, 'y': 1.2295167303380952, 'z': - 3.3842206934036057 },
		{ 'x': - 2.4019084876611916, 'y': - 2.190220428765315, 'z': 3.7991801866087123 },
		{ 'x': - 2.49363689878109, 'y': - 4.099696049856375, 'z': 1.4050862307450966 },
		{ 'x': - 2.3729307780326305, 'y': 2.840227787960863, 'z': 3.3618901878497454 },
		{ 'x': - 2.0636200279017873, 'y': 0.7444294629976027, 'z': - 4.493027615657812 },
		{ 'x': 0.47725894517680106, 'y': 2.4327372143508037, 'z': - 4.34212085796347 },
		{ 'x': - 2.4777001955161246, 'y': - 1.2092952460724242, 'z': 4.171163716394502 },
		{ 'x': - 0.03915748918627658, 'y': - 0.008362945319338826, 'z': 4.999839672648135 },
		{ 'x': 1.5223738738260317, 'y': - 1.032865814102439, 'z': - 4.649254348640267 },
		{ 'x': - 0.26640112020426315, 'y': - 4.314854187280748, 'z': 2.5121830716848077 },
		{ 'x': - 4.031470206741836, 'y': - 2.606648761952297, 'z': - 1.3973654511134501 },
		{ 'x': 0.8544382232162094, 'y': 1.5274953155132989, 'z': 4.683662390031124 },
		{ 'x': 3.0409624989238546, 'y': 1.76433738825175, 'z': - 3.555230043268055 },
		{ 'x': - 4.721251023266457, 'y': 1.2354922989397954, 'z': - 1.0878177947459262 },
		{ 'x': 2.1518961827021106, 'y': 3.891904027152385, 'z': - 2.285262755638206 },
		{ 'x': 0.8501960736517479, 'y': - 2.851729208821255, 'z': - 4.018060123480341 },
		{ 'x': 2.5631840141785176, 'y': 4.263234820997851, 'z': - 0.5048926326370041 },
		{ 'x': - 0.4580143454812531, 'y': - 2.6523265200067385, 'z': 4.213714144386437 }
	];

	function getPos( radius, a, b ) {

		var x = radius * Math.sin( a ) * Math.cos( b );
		var y = radius * Math.sin( a ) * Math.sin( b );
		var z = radius * Math.cos( a );
		return { x, y, z };
	}

	//threejs自带的经纬度转换
	function lglt2xyz( lng, lat ) {
		const theta = ( 90 + lng ) * ( Math.PI / 180 );
		const phi = ( 90 - lat ) * ( Math.PI / 180 );
		return ( new THREE.Vector3() ).setFromSpherical( new THREE.Spherical( radius, phi, theta ) );
	}

	/**
	 * 经纬度坐标转球面坐标
	 * @param {地球半径} R
	 * @param {经度(角度值)} longitude
	 * @param {维度(角度值)} latitude
	 */
	function lon2xyz( R, longitude, latitude ) {
		var lon = longitude * Math.PI / 180;//转弧度值
		var lat = latitude * Math.PI / 180;//转弧度值
		lon = - lon;// three.js坐标系z坐标轴对应经度-90度，而不是90度
		// 经纬度坐标转球面坐标计算公式
		var x = R * Math.cos( lat ) * Math.cos( lon );
		var y = R * Math.sin( lat );
		var z = R * Math.cos( lat ) * Math.sin( lon );
		// 返回球面坐标
		return {
			x: x,
			y: y,
			z: z,
		};
	}

	/**
	 * @desc 随机设置点
	 * @param <Group> group ...
	 * @param <number> radius ...
	 */
	function setRandomDot( group ) {
		var texture = new THREE.TextureLoader().load( './imgs/diqiu2/标注.webp' );
		var texture2 = new THREE.TextureLoader().load( './imgs/diqiu2/标注光圈.webp' );
		posArr.map( pos => {
			var dotMesh = createPointMesh( pos, texture );
			var waveMesh = createWaveMesh( pos, texture2 );
			group.add( dotMesh );
			group.add( waveMesh );
			WaveMeshArr.push( waveMesh );
		} );
	}

	/**
	 * 标注
	 */
	function createPointMesh( pos, texture ) {
		var material = new THREE.MeshBasicMaterial( {
			map: texture,
			transparent: true, //使用背景透明的png贴图，注意开启透明计算
			// side: THREE.DoubleSide, //双面可见
			depthWrite: false, //禁止写入深度缓冲区数据
		} );
		var mesh = new THREE.Mesh( planGeometry, material );
		var size = radius * 0.04;//矩形平面Mesh的尺寸
		mesh.scale.set( size, size, size );//设置mesh大小
		//设置mesh位置
		mesh.position.set( pos.x, pos.y, pos.z );
		// mesh在球面上的法线方向(球心和球面坐标构成的方向向量)
		var coordVec3 = new THREE.Vector3( pos.x, pos.y, pos.z ).normalize();
		// mesh默认在XOY平面上，法线方向沿着z轴new THREE.Vector3(0, 0, 1)
		var meshNormal = new THREE.Vector3( 0, 0, 1 );
		// 四元数属性.quaternion表示mesh的角度状态
		//.setFromUnitVectors();计算两个向量之间构成的四元数值
		mesh.quaternion.setFromUnitVectors( meshNormal, coordVec3 );
		return mesh;
	}

	/**
	 * 标注的光圈
	 */
	function createWaveMesh( pos, texture ) {
		var material = new THREE.MeshBasicMaterial( {
			color: 0x22ffcc,
			map: texture,
			transparent: true, //使用背景透明的png贴图，注意开启透明计算
			opacity: 1.0,
			// side: THREE.DoubleSide, //双面可见
			depthWrite: false, //禁止写入深度缓冲区数据
		} );
		var mesh = new THREE.Mesh( planGeometry, material );
		// var coord = lon2xyz(R*1.001, lon, lat)
		var size = radius * 0.055;//矩形平面Mesh的尺寸
		mesh.size = size;//自顶一个属性，表示mesh静态大小
		mesh.scale.set( size, size, size );//设置mesh大小
		mesh._s = Math.random() * 1.0 + 1.0;//自定义属性._s表示mesh在原始大小基础上放大倍数  光圈在原来mesh.size基础上1~2倍之间变化
		mesh.position.set( pos.x, pos.y, pos.z );
		// mesh姿态设置
		// mesh在球面上的法线方向(球心和球面坐标构成的方向向量)
		var coordVec3 = new THREE.Vector3( pos.x, pos.y, pos.z ).normalize();
		// mesh默认在XOY平面上，法线方向沿着z轴new THREE.Vector3(0, 0, 1)
		var meshNormal = new THREE.Vector3( 0, 0, 1 );
		// 四元数属性.quaternion表示mesh的角度状态
		//.setFromUnitVectors();计算两个向量之间构成的四元数值
		mesh.quaternion.setFromUnitVectors( meshNormal, coordVec3 );
		return mesh;
	}

	// 添加飞线
	function addLines( v0, v3 ) {
		// 夹角
		var angle = ( v0.angleTo( v3 ) * 1.8 ) / Math.PI / 0.1; // 0 ~ Math.PI
		var aLen = angle * 0.4, hLen = angle * angle * 12;
		var p0 = new THREE.Vector3( 0, 0, 0 );
		// 法线向量
		var rayLine = new THREE.Ray( p0, getVCenter( v0.clone(), v3.clone() ) );
		// 顶点坐标
		var vtop = rayLine.at( hLen / rayLine.at( 1 ).distanceTo( p0 ) );
		// 控制点坐标
		var v1 = getLenVcetor( v0.clone(), vtop, aLen );
		var v2 = getLenVcetor( v3.clone(), vtop, aLen );
		// 绘制三维三次贝赛尔曲线
		var curve = new THREE.CubicBezierCurve3( v0, v1, v2, v3 );
		var geometry = new LineGeometry();
		var points = curve.getSpacedPoints( 50 );
		var positions = [];
		var colors = [];
		var color = new THREE.Color();
		/**
		 * HSL中使用渐变
		 * h — hue value between 0.0 and 1.0
		 * s — 饱和度 between 0.0 and 1.0
		 * l — 亮度 between 0.0 and 1.0
		 */
		for (var j = 0; j < points.length; j ++) {
      color.setHSL(0.675, 0.324, 0.55); //黑色
			// color.setHSL( .81666+j,0.88, 0.715+j*0.0025); //粉色
			colors.push( color.r, color.g, color.b );
			positions.push( points[j].x, points[j].y, points[j].z );
		}
		geometry.setPositions( positions );
		geometry.setColors( colors );
		var matLine = new LineMaterial( {
			linewidth: 0.0006,
			vertexColors: true,
			dashed: false
		} );

		return {
			curve: curve,
			lineMesh: new Line2( geometry, matLine )
		};
	}

	// 计算v1,v2 的中点
	function getVCenter( v1, v2 ) {
		const v = v1.add( v2 );
		return v.divideScalar( 2 );
	}

	// 计算V1，V2向量固定长度的点
	function getLenVcetor( v1, v2, len ) {
		const v1v2Len = v1.distanceTo( v2 );
		return v1.lerp( v2, len / v1v2Len );
	}

	/**
	 * @description 初始化渲染场景
	 */
	function initRenderer() {
		renderer = new THREE.WebGLRenderer( { antialias: true, alpha: true } );
		renderer.setPixelRatio( window.devicePixelRatio );
		renderer.setSize( width, height );
		const containerDom = document.querySelector( '#container' );
		containerDom.appendChild( renderer.domElement );
	}

	/**
	 * @description 初始化相机
	 */
	function initCamera() {
		camera = new THREE.PerspectiveCamera( 45, width / height, 1, 10000 );
		camera.position.set( 5, - 20, 200 );
		camera.lookAt( 0, 3, 0 );
		window.camera = camera;
	}

	/**
	 * @description 初始化场景
	 */
	function initScene() {
		scene = new THREE.Scene();
		scene.background = new THREE.Color( 0x020924 );
		scene.fog = new THREE.Fog( 0x020924, 200, 1000 );
		window.scene = scene;
	}

	//辅助工具
	function initAuxiliaryTool() {
		const helper = new THREE.AxesHelper( 500 );
		scene.add( helper );
		// 网格
		var grid = new THREE.GridHelper( 2, 10, 0x000000, 0x000000 );
		grid.material.opacity = 0.2;
		grid.material.transparent = true;
		scene.add( grid );
	}

	//性能插件
	function initStats() {
		stats = new Stats();
		document.body.appendChild( stats.dom );
	}

	/**
	 * 初始化用户交互
	 **/
	function initControls() {
		controls = new OrbitControls( camera, renderer.domElement );
		// 如果使用animate方法时，将此函数删除
		// controls.addEventListener( 'change', render );
		// 使动画循环使用时阻尼或自转 意思是否有惯性
		controls.enableDamping = true;
		//动态阻尼系数 就是鼠标拖拽旋转灵敏度
		//controls.dampingFactor = 0.25;
		//是否可以缩放
		controls.enableZoom = true;
		//是否自动旋转
		controls.autoRotate = false;
		controls.autoRotateSpeed = 2;
		//设置相机距离原点的最远距离
		// controls.minDistance = 2;
		//设置相机距离原点的最远距离
		// controls.maxDistance = 1000;
		//是否开启右键拖拽
		controls.enablePan = true;
	}

	function initTween() {
		var tweena = cameraCon( 3000 );
		tweena.start();
	}

	function initGui() {
		gui = new GUI();
		gui.add( API, 'c', - 10, 10 ).name( 'c' ).onChange( updateUvTransform );
		gui.add( API, 'p', - 10, 10 ).name( 'p' ).onChange( updateUvTransform );
		gui.addColor( API, 'color' ).name( '颜色' ).onChange( updateUvTransform );
	}

	function updateUvTransform() {
		uniforms.c.value = API.c;
		uniforms.p.value = API.p;
		uniforms.glowColor.value = new THREE.Color( API.color );
		renders();
	}

	function cameraCon( time ) {
		var tween1 = new TWEEN.Tween( camaPositions[0] ).to( camaPositions[1], time ).easing( TWEEN.Easing.Quadratic.InOut );
		var update = () => {
			camera.position.set( camaPositions[0].x, camaPositions[0].y, camaPositions[0].z );
		};
		tween1.onUpdate( update );
		tween1.onComplete( function () {
			initFlag = true;
			//初始化点和曲线
			initDotAndFly();
			//光柱效果和底部矩形
			initLightPillar();
		} );
		return tween1;
	}

	// 经纬度转地球坐标
	function createPosition( lnglat ) {
		const spherical = new THREE.Spherical();
		spherical.radius = radius;
		const lng = lnglat[0];
		const lat = lnglat[1];
		const theta = ( lng + 90 ) * ( Math.PI / 180 );
		const phi = ( 90 - lat ) * ( Math.PI / 180 );
		spherical.phi = phi; //方位角
		spherical.theta = theta; //倾斜角
		const position = new THREE.Vector3();
		position.setFromSpherical( spherical );
		return position;
	}

	/**
	 * @description 初始化光
	 */
	function initLight() {
		const ambientLight = new THREE.AmbientLight( 0xcccccc, 1.1 );
		scene.add( ambientLight );
		var directionalLight = new THREE.DirectionalLight( 0xffffff, 0.2 );
		directionalLight.position.set( 1, 0.1, 0 ).normalize();
		var directionalLight2 = new THREE.DirectionalLight( 0xff2ffff, 0.2 );
		directionalLight2.position.set( 1, 0.1, 0.1 ).normalize();
		scene.add( directionalLight );
		scene.add( directionalLight2 );
		var hemiLight = new THREE.HemisphereLight( 0xffffff, 0x444444, 0.2 );
		hemiLight.position.set( 0, 1, 0 );
		scene.add( hemiLight );
		var directionalLight = new THREE.DirectionalLight( 0xffffff );
		directionalLight.position.set( 1, 500, - 20 );
		directionalLight.castShadow = true;
		directionalLight.shadow.camera.top = 18;
		directionalLight.shadow.camera.bottom = - 10;
		directionalLight.shadow.camera.left = - 52;
		directionalLight.shadow.camera.right = 12;
		scene.add(directionalLight);
	}

	/**
	 * 初始化背景星空
	 */
	function initPoints() {
		var texture = new THREE.TextureLoader().load( './imgs/diqiu2/gradient.webp' );
		const positions = [];
		const colors = [];
		const geometry = new THREE.BufferGeometry();
		for (var i = 0; i < 10000; i ++) {
			var vertex = new THREE.Vector3();
			vertex.x = Math.random() * 2 - 1;
			vertex.y = Math.random() * 2 - 1;
			vertex.z = Math.random() * 2 - 1;
			positions.push( vertex.x, vertex.y, vertex.z );
			var color = new THREE.Color();
			color.setHSL( Math.random() * 0.2 + 0.5, 0.55, Math.random() * 0.25 + 0.55 );
			colors.push( color.r, color.g, color.b );
		}
		geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) );
		geometry.setAttribute( 'color', new THREE.Float32BufferAttribute( colors, 3 ) );
		var starsMaterial = new THREE.ParticleBasicMaterial( {
			map: texture,
			size: 1,
			transparent: true,
			opacity: 1,
			vertexColors: true, //true：且该几何体的colors属性有值，则该粒子会舍弃第一个属性--color，而应用该几何体的colors属性的颜色
			blending: THREE.AdditiveBlending,
			sizeAttenuation: true
		} );
		stars = new THREE.ParticleSystem( geometry, starsMaterial );
		stars.scale.set( 300, 300, 300 );
		scene.add( stars );
	}

	/**
	 * 包含2个，一个地球，一个辉光球体
	 */
	function initEarth() {
		// 地球
		globeTextureLoader.load( './imgs/diqiu2/earth_3.webp', function ( texture ) {
			var globeGgeometry = new THREE.SphereGeometry( radius, 100, 100 );
			var globeMaterial = new THREE.MeshStandardMaterial( { map: texture,side:THREE.DoubleSide } );
			var globeMesh = new THREE.Mesh( globeGgeometry, globeMaterial );
			group.rotation.set( 0.5, 2.9, 0.1 );
			group.add( globeMesh );
			scene.add( group );
		} );
	}

	/**
	 * 包含2个，一个地球，一个辉光球体
	 */
	function initEarth2() {
		uniforms = {
			u_time: { value: 0.0 },
			c: { type: 'f', value: 2.3 }, //2.1 								// 系数
			p: { type: 'f', value: 1.7 }, //1.7 								// 强度
			diffuse: { // 用于实现扫描效果的贴图
				type: 't',
				value: new THREE.TextureLoader().load( './imgs/diqiu2/earth_3.webp' )
			},
			glowColor: { type: 'c', value: new THREE.Color( 0x10105 ) }, /// 光罩的颜色
			viewVector: { // 相机位置
				type: 'v3', value: new THREE.Vector3( 0, 0, 1 )
			},
			blending: THREE.AdditiveBlending
		};
		uniforms['diffuse'].value.wrapS = uniforms['diffuse'].value.wrapT = THREE.RepeatWrapping;
		var material = new THREE.ShaderMaterial( {
			uniforms: uniforms,
			vertexShader: document.getElementById( 'vertexShader' ).textContent,
			fragmentShader: document.getElementById( 'fragmentShader' ).textContent,
			side: THREE.DoubleSide,
			// depthWrite: false,
			// transparent: true,
		} );
		var globeGgeometry = new THREE.SphereGeometry( radius, 100, 100 );
		var globeMesh = new THREE.Mesh( globeGgeometry, material );
		group.rotation.set( 0.5, 2.9, 0.1 );
		group.add( globeMesh );
		scene.add( group );
	}

	/**
	 * 创建地球光晕特效
	 */
	function initEarthSprite() {
		var texture = globeTextureLoader.load( './imgs/diqiu2/earth_aperture.webp' );
		var spriteMaterial = new THREE.SpriteMaterial( {
			map: texture,
			transparent: true,
			opacity: 0.5,
			depthWrite: false
		} );
		var sprite = new THREE.Sprite( spriteMaterial );
		sprite.scale.set( radius * 3, radius * 3, 1 );
		group.add( sprite );
	}

	/**
	 * 添加卫星特效
	 */
	function initSatellite() {
		// 光环
		globeTextureLoader.load( './imgs/diqiu2/halo.webp', function ( texture ) {
			var geometry = new THREE.PlaneGeometry( 14, 14 );//矩形平面
			var material = new THREE.MeshLambertMaterial( {
				map: texture, //给纹理属性map赋值
				transparent: true,
				side: THREE.DoubleSide, //两面可见
				depthWrite: false
			} );//材质对象
			var mesh = new THREE.Mesh( geometry, material );//网格模型对象
			groupHalo.add( mesh );
		} );
		// 小地球
		globeTextureLoader.load( './imgs/diqiu2/smallEarth.webp', function ( texture ) {
			var p1 = new THREE.Vector3( - 7, 0, 0 );//顶点1坐标
			var p2 = new THREE.Vector3( 7, 0, 0 );//顶点2坐标
			const points = [ p1, p2 ];
			const geometry = new THREE.BufferGeometry().setFromPoints( points );
			var material = new THREE.PointsMaterial( {
				map: texture, //给纹理属性map赋值
				transparent: true,
				side: THREE.DoubleSide, //两面可见
				size: 1, //点对象像素尺寸
				depthWrite: false
			} );//材质对象
			var earthPoints = new THREE.Points( geometry, material );//点模型对象
			groupHalo.add( earthPoints );//点对象添加到场景中
		} );
		groupHalo.rotation.set( 1.9, 0.5, 1 );
		scene.add( groupHalo );
	}

	/**
	 * 光柱特效
	 */
	function createLightPillar( pos ) {
		var height = radius * 0.1;//光柱高度，和地球半径相关，这样调节地球半径，光柱尺寸跟着变化
		var geometry = new THREE.PlaneBufferGeometry( radius * 0.05, height ); //默认在XOY平面上
		geometry.rotateX( Math.PI / 2 );//光柱高度方向旋转到z轴上
		geometry.translate( 0, 0, height / 2 );//平移使光柱底部与XOY平面重合
		var textureLoader = new THREE.TextureLoader(); // TextureLoader创建一个纹理加载器对象
		var material = new THREE.MeshBasicMaterial( {
			map: textureLoader.load( './imgs/diqiu2/光柱.webp' ),
			color: 0x44ffaa, //光柱颜色，光柱map贴图是白色，可以通过color调节颜色
			transparent: true, //使用背景透明的png贴图，注意开启透明计算
			side: THREE.DoubleSide, //双面可见
			depthWrite: false, //是否对深度缓冲区有任何的影响
		} );
		var mesh = new THREE.Mesh( geometry, material );
		var group = new THREE.Group();
		// 两个光柱交叉叠加
		group.add( mesh, mesh.clone().rotateZ( Math.PI / 2 ) );//几何体绕x轴旋转了，所以mesh旋转轴变为z
		group.position.set( pos.x, pos.y, pos.z );//设置mesh位置
		var coordVec3 = new THREE.Vector3( pos.x, pos.y, pos.z ).normalize();
		var meshNormal = new THREE.Vector3( 0, 0, 1 );
		// 四元数属性.quaternion表示mesh的角度状态
		//.setFromUnitVectors();计算两个向量之间构成的四元数值
		group.quaternion.setFromUnitVectors( meshNormal, coordVec3 );
		return group;
	}

	/**
	 * 光柱底部的矩形平面特效
	 */
	function createLightWaveMesh( pos, texture ) {
		var geometry = new THREE.PlaneBufferGeometry( 1, 1 ); //默认在XOY平面上
		var material = new THREE.MeshBasicMaterial( {
			color: 0x22ffcc,
			map: texture,
			transparent: true, //使用背景透明的png贴图，注意开启透明计算
			// side: THREE.DoubleSide, //双面可见
			depthWrite: false, //禁止写入深度缓冲区数据
		} );
		var mesh = new THREE.Mesh( geometry, material );
		var size = radius * 0.05;//矩形平面Mesh的尺寸
		mesh.scale.set( size, size, size );//设置mesh大小
		return mesh;
	}

	/**
	 * 光柱效果
	 */
	function initLightPillar() {
		var texture = new THREE.TextureLoader().load( './imgs/diqiu2/标注.webp' );
		var datas = [
			{
				lng: 86.39895905468748, lat: 45.15923349468924 //合肥
			},
			{
				lng: 106.54041, lat: 29.40268 //重庆
			}
		];
		datas.forEach( function ( obj ) {
			var pos = lglt2xyz( obj.lng, obj.lat );
			var LightPillar = createLightPillar( pos );
			groupDots.add( LightPillar );
			var waveMesh = createLightWaveMesh( pos, texture );
			LightPillar.add( waveMesh );
		} );
	}

	/**
	 * @description 初始化点和曲线
	 */
	function initDotAndFly() {
		// 创建标注点
		setRandomDot( groupDots );
		//随机点加载group上面
		group.add( groupDots );
		// 曲线
		var animateDots = [];
		console.info("第一个坐标是多少")
		console.info(groupDots.children[0].position)
		groupDots.children.forEach( elem => {
			if (groupDots.children[0].position.x == elem.position.x) {
				return true;
			}
			var line = addLines( groupDots.children[0].position, elem.position );
			groupLines.add( line.lineMesh );
			animateDots.push( line.curve.getPoints( 100 ) ); //这个是里面球
		} );
		group.add( groupLines );
		// 添加动画
		for (let i = 0; i < animateDots.length; i ++) {
			const aGeo = new THREE.SphereGeometry( 0.03, 0.03, 0.03 );
			const aMater = new THREE.MeshPhongMaterial( { color: '#F8D764' } );
			const aMesh = new THREE.Mesh( aGeo, aMater );
			aGroup.add( aMesh );
		}
		var vIndex = 0;
		function animateLine() {
			aGroup.children.forEach( ( elem, index ) => {
				const v = animateDots[index][vIndex];
				elem.position.set( v.x, v.y, v.z );
			} );
			vIndex ++;
			if (vIndex > 100) {
				vIndex = 0;
			}
			setTimeout( animateLine, 20 );
		}
		group.add( aGroup );
		animateLine();
	}

	/**
	 * 中国描边高亮
	 */
	function initGeoJson() {
		const loader = new THREE.FileLoader();
		loader.load( './models/json/geoJson/china.json', function ( data ) {
			const jsonData = JSON.parse( data );
			initMap( jsonData );
		} );
		loader.load( './models/json/geoJson/china-outline.json', function ( data ) {
			const jsonData = JSON.parse( data );
			outLineMap( jsonData );
		} );
	}

	function outLineMap( json ) {
		json.features.forEach( elem => {
			// 新建一个省份容器：用来存放省份对应的模型和轮廓线
			const province = new THREE.Object3D();
			const coordinates = elem.geometry.coordinates;
			coordinates.forEach( multiPolygon => {
				multiPolygon.forEach( polygon => {
					// 这里的坐标要做2次使用：1次用来构建模型，1次用来构建轮廓线
					if (polygon.length > 200) {
						var v3ps = [];
						for (let i = 0; i < polygon.length; i ++) {
							var pos = lglt2xyz( polygon[i][0], polygon[i][1] );
							v3ps.push( pos );
						}
						var curve = new THREE.CatmullRomCurve3( v3ps, false/*是否闭合*/ );
						var color = new THREE.Vector3( 0.5999758518718452, 0.7798940272761521, 0.6181903838257632 );
						var flyLine = initFlyLine( curve, {
							speed: 0.4,
							// color: randomVec3Color(),
							color: color,
							number: 3, //同时跑动的流光数量
							length: 0.2, //流光线条长度
							size: 2 //粗细
						}, 5000 );
						province.add( flyLine );
					}
				} );
			} );
			map.add( province );
		} );
		group.add( map );
	}

	function initMap( chinaJson ) {
		// 遍历省份构建模型
		chinaJson.features.forEach( elem => {
			// 新建一个省份容器：用来存放省份对应的模型和轮廓线
			const province = new THREE.Object3D();
			const coordinates = elem.geometry.coordinates;
			coordinates.forEach( multiPolygon => {
				multiPolygon.forEach( polygon => {
					const lineMaterial = new THREE.LineBasicMaterial( { color: 0XF19553 } ); //0x3BFA9E  0XF19553
					const positions = [];
					const linGeometry = new THREE.BufferGeometry();
					for (let i = 0; i < polygon.length; i ++) {
						var pos = lglt2xyz( polygon[i][0], polygon[i][1] );
						positions.push( pos.x, pos.y, pos.z );
					}
					linGeometry.setAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) );
					const line = new THREE.Line( linGeometry, lineMaterial );
					province.add( line );
				} );
			} );
			map.add( province );

		} );
		group.add( map );
	}

	/**
	 * @param curve {THREE.Curve} 路径,
	 * @param matSetting {Object} 材质配置项
	 * @param pointsNumber {Number} 点的个数 越多越细致
	 * */
	function initFlyLine( curve, matSetting, pointsNumber ) {
		var points = curve.getPoints( pointsNumber );
		var geometry = new THREE.BufferGeometry().setFromPoints( points );
		const length = points.length;
		var percents = new Float32Array( length );
		for (let i = 0; i < points.length; i += 1) {
			percents[i] = ( i / length );
		}
		geometry.addAttribute( 'percent', new THREE.BufferAttribute( percents, 1 ) );
		const lineMaterial = initLineMaterial( matSetting );
		var flyLine = new THREE.Points( geometry, lineMaterial );
		return flyLine;
	}

	function initLineMaterial( setting ) {
		const number = setting ? ( Number( setting.number ) || 1.0 ) : 1.0;
		const speed = setting ? ( Number( setting.speed ) || 1.0 ) : 1.0;
		const length = setting ? ( Number( setting.length ) || 0.5 ) : 0.5;
		const size = setting ? ( Number( setting.size ) || 3.0 ) : 3.0;
		const color = setting ? setting.color || new THREE.Vector3( 0, 1, 1 ) : new THREE.Vector3( 0, 1, 1 );
		const singleUniforms = {
			u_time: uniforms2.u_time,
			number: { type: 'f', value: number },
			speed: { type: 'f', value: speed },
			length: { type: 'f', value: length },
			size: { type: 'f', value: size },
			color: { type: 'v3', value: color }
		};
		const lineMaterial = new THREE.ShaderMaterial( {
			uniforms: singleUniforms,
			vertexShader: document.getElementById( 'vertexShader2' ).textContent,
			fragmentShader: document.getElementById( 'fragmentShader2' ).textContent,
			transparent: true,
			//blending:THREE.AdditiveBlending,
		} );
		return lineMaterial;
	}

	/**
	 * 窗口变动
	 **/
	function onWindowResize() {
		camera.aspect = innerWidth / innerHeight;
		camera.updateProjectionMatrix();
		renders();
		renderer.setSize( innerWidth, innerHeight );
	}

	/**
	 * @description 渲染
	 */
	function renders() {
		renderer.clear();
		renderer.render( scene, camera );
	}

	/**
	 * 更新
	 **/
	function animate() {
		window.requestAnimationFrame( () => {
			if (controls) controls.update();
			if (stats) stats.update();
			if (TWEEN) TWEEN.update();
			if (initFlag) {
				//光环
				groupHalo.rotation.z = groupHalo.rotation.z + 0.01;
				group.rotation.y = group.rotation.y + 0.001;
				// 所有波动光圈都有自己的透明度和大小状态
				// 一个波动光圈透明度变化过程是：0~1~0反复循环
				if (WaveMeshArr.length) {
					WaveMeshArr.forEach( function ( mesh ) {
						mesh._s += 0.007;
						mesh.scale.set( mesh.size * mesh._s, mesh.size * mesh._s, mesh.size * mesh._s );
						if (mesh._s <= 1.5) {
							//mesh._s=1，透明度=0 mesh._s=1.5，透明度=1
							mesh.material.opacity = ( mesh._s - 1 ) * 2;
						} else if (mesh._s > 1.5 && mesh._s <= 2) {
							//mesh._s=1.5，透明度=1 mesh._s=2，透明度=0
							mesh.material.opacity = 1 - ( mesh._s - 1.5 ) * 2;
						} else {
							mesh._s = 1.0;
						}
					} );
				}
			}
			if (stars) {
				stars.rotation.y += .0001;
			}
			uniforms2.u_time.value += 0.007;
			renders();
			animate();
		} );
	}

	window.onload = () => {
		initRenderer();
		initCamera();
		initScene();
		initLight();
		// initAuxiliaryTool();
		// initStats();
		//初始化地球
		initEarth();
		// initEarth2(); //地球2使用菲尼尔反射效果，可以配合GUI使用，使用记得开启initGui()方法注释
		//卫星特效
		initSatellite();
		//地球光晕
		initEarthSprite();
		//初始化动态星空背景
		initPoints();
		//初始化点和曲线，放在tween动画完成后调用
		// initDotAndFly();

		//外圈中国描边高亮
		initGeoJson();
		initControls();
		initTween();
		// initGui();
		animate();
		window.addEventListener('resize', onWindowResize, false);
	};

</script>
</body>
</html>
